﻿#pragma once

#include "qevent.h"

namespace micro{ 
namespace core{
 
 
 
// Thread function definition.
//typedef UINT ( WINAPI *LPTHREAD_PROC )( LPVOID );

 
class QWorkerThread
{
	//_NO_COPY( QWorkerThread );
public:
	// Contruction.
	QWorkerThread(): m_shutEvent(TRUE, FALSE), m_runEvent(TRUE, TRUE)
	{
		m_Handles[ 0 ] = m_shutEvent;		
		m_Handles[ 1 ] = m_runEvent;

		Init();
	}

	void Init()
	{	
		m_hThread = nullptr;
		m_bRunning = false;
		m_bPaused  = false;
		
		m_shutEvent.ResetEvent();
		m_runEvent.SetEvent();  
		
		
	}
	void Clean()
	{
		if ( m_hThread )
		{
			// Stop the thread if not done yet.
			m_shutEvent.SetEvent();

			// Wait for the thread to stop.
			::WaitForSingleObject( m_hThread, 100 );
			::CloseHandle( m_hThread );
			
			//m_hThread = nullptr;

			//m_bRunning = false;
			//m_bPaused  = false;
			
			Init();
		}
	}


	// Destruction.
	virtual ~QWorkerThread()
	{					
			Clean();	 		
	}
	bool SetPriority(int priority) { return 0 != ::SetThreadPriority(m_hThread, priority); }

	// Stop the thread.
	void Stop()
	{
		// Is it running?
		if ( m_bRunning )
		{
			// Signal the shutdown event.
			m_shutEvent.SetEvent();

			//let the thread to exit pause state end terminate
			//m_runEvent.SetEvent();  

			// Wait for the thread to die...
			//::WaitForSingleObject( m_hThread, 100 );

			// Close the handle.
			//::CloseHandle( m_hThread );
			//m_hThread = nullptr;
			
			m_bRunning = false;			
			m_bPaused  = false;		
			
		}
	}
 
	// The thread should call this code at regular intervals to 
	// determine what action it should take depending on the return 
	// codes as defined above.
	//
	// false	- Thread runs, no problems.
	// true		- The thread has received a shutdown signal. Exit the
	 
	bool StopWork()
	{
		// Wait for the shutdown and running
		// events.
		m_bPaused = true;
		
		
		//trece daca unul din events este deschis(setEvent)
		//daca amandoua sunt inchise(ResetEvent) se blocheaza si asteapta, inseamna ca s-a apasat Pause
		switch ( ::WaitForMultipleObjects( 2, m_Handles, FALSE, INFINITE ))
		{
			case	WAIT_OBJECT_0:
				// Shutdown event was set.
				m_bRunning = false;

				return true;//stop work 

			case	WAIT_OBJECT_0 + 1:				
				m_bPaused = false;
				return false; //do not stop, go ahead
		}
		return true; //error, stop
	}


	virtual DWORD Run() = 0;
	static DWORD  WINAPI StaticRun(LPVOID p) { return ((QWorkerThread*)p)->Run(); }

	// Start the thread.
	bool Start(  )
	{
		if(m_bRunning)
			return false;
 
		//se presupune ca firul e oprit 
		Clean();
		
		m_hThread = CreateThread( nullptr, 0,  (LPTHREAD_START_ROUTINE)StaticRun, this , 0, &m_dwThreadID );
 
		if ( m_hThread )
		{
			// If this times-out the thread is up and running. If it returns
			// WAIT_OBJECT_0 the thread has exited before reaching this code.
			switch ( ::WaitForSingleObject( m_hThread, 0 ))
			{
				case	WAIT_TIMEOUT:
					m_bRunning = true;
					return true;

				case	WAIT_OBJECT_0: //m_shutEvent is open
					::CloseHandle( m_hThread );
					m_hThread = nullptr;
					return false;
			}
		}
		
		return false;
	}

	

	// Pause/resume.
	inline void Pause()	{ if ( m_bRunning )	{ m_bPaused = true;  m_runEvent.ResetEvent(); } }
	inline void Resume()	{ if ( m_bRunning ) m_runEvent.SetEvent();   }
	inline bool IsRunning() { return m_bRunning; }
	inline bool IsPaused()  { return m_bPaused; }

protected:
 
	 
	// Data.
	QEvent      m_shutEvent;
	QEvent		m_runEvent;

	HANDLE		m_Handles[ 2 ];

	HANDLE		m_hThread;

	DWORD		m_dwThreadID;


	bool		m_bRunning;
	bool		m_bPaused;
};


}
}//micro